package hh.util.bean.codegen;

import hh.asm.AsmUtil;
import hh.util.bean.ClassInfo;
import hh.util.bean.ReflectFieldInfo;
import hh.util.bean.copy.test.User1;
import org.objectweb.asm.*;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;

public class CGFull0Dump implements Opcodes {

    public static class MyClassLoader extends ClassLoader {
        public Class<?> loadClass(String name, byte[] b) {
            return super.defineClass(name, b, 0, b.length, null);
        }
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader loader = new MyClassLoader();

        Class<?> proxy = loader.loadClass("hh.util.beanutil.codegen.CGFull0", generateCopyProxyClassBytes(null,null,null));
        // JavaBeanCreator creator = JavaBeanCreatorFactory.getObject(proxy);
        FullCodeGenerateCopyProxy copyProxy = (FullCodeGenerateCopyProxy) proxy.newInstance();
        copyProxy.setJavaBeanCreator(JavaBeanCreatorFactory.getObject(User1.class));
        System.out.println(copyProxy);

    }

    public static byte[] generateCopyProxyClassBytes(String className,ClassInfo sourceClassInfo, ClassInfo destClassInfo) {

        Map<String, ReflectFieldInfo> sourceFieldMap = sourceClassInfo.getNameToFieldInfo();
        // 目标类有的field有set方法，source类有对应的field并且有get方法，并且source dest类型匹配
        ReflectFieldInfo[] fieldInfos = Arrays.stream(destClassInfo.getFieldInfos())
                .filter(destFieldInfo -> {
                    Field destField = destFieldInfo.getField();
                    ReflectFieldInfo sourceFieldInfo = sourceFieldMap.get(destField.getName());
                    boolean hasSourceAndDestAndTypeMatch = sourceFieldInfo != null && destFieldInfo.hasSetter() && sourceFieldInfo.hasGetter()
                            && destField.getType() == sourceFieldInfo.getField().getType();
                    return hasSourceAndDestAndTypeMatch;
                }).toArray(ReflectFieldInfo[]::new);

        String unixClassName = AsmUtil.getClassName(className);
        //Config.codeGenPackage + "." + Config.getCodeGenClassNameIndex()).replace(".", "/");

        String unixInterfaceName = FullCodeGenerateCopyProxy.class.getName().replace(".", "/");
        String objectType = AsmUtil.getType(Object.class);
        String copyProxyType = AsmUtil.getType(FullCodeGenerateCopyProxy.class,destClassInfo.getClazz());



        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        cw.visit(52, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, unixClassName, objectType+copyProxyType,
                AsmUtil.getClassName(Object.class), new String[]{AsmUtil.getClassName(FullCodeGenerateCopyProxy.class)});

        {
            fv = cw.visitField(0, "creator", "Lhh/util/beanutil/codegen/JavaBeanCreator;", null, null);
            fv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "setJavaBeanCreator", "(Lhh/util/beanutil/codegen/JavaBeanCreator;)V", null, null);
            mv.visitParameter("creator", 0);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitFieldInsn(PUTFIELD, "hh/util/bean/codegen/CGFull0", "creator", "Lhh/util/beanutil/codegen/JavaBeanCreator;");
            mv.visitInsn(RETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "copy", "(Lhh/util/beanutil/copy/test/User;)Ljava/lang/Object;", null, null);
            mv.visitParameter("source", 0);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, unixClassName, "creator", "Lhh/util/beanutil/codegen/JavaBeanCreator;");
            mv.visitMethodInsn(INVOKEINTERFACE, "hh/util/bean/codegen/JavaBeanCreator", "newObject", "()Ljava/lang/Object;", true);
            mv.visitTypeInsn(CHECKCAST, "hh/util/bean/copy/test/User1");
            mv.visitVarInsn(ASTORE, 2);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getA", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setA", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getB", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setB", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getC", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setC", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getD", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setD", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getE", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setE", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getF", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setF", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getG", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setG", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getH", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setH", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getI", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setI", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getJ", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setJ", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getK", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setK", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getL", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setL", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getM", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setM", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getN", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setN", "(Ljava/lang/String;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getO", "()I", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setO", "(I)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getP", "()Ljava/lang/Integer;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setP", "(Ljava/lang/Integer;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getQ", "()J", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setQ", "(J)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getR", "()Ljava/lang/Long;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setR", "(Ljava/lang/Long;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getS", "()D", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setS", "(D)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User", "getT", "()Ljava/lang/Double;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/copy/test/User1", "setT", "(Ljava/lang/Double;)V", false);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(3, 3);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "copy", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
            mv.visitParameter("source", ACC_SYNTHETIC);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitTypeInsn(CHECKCAST, "hh/util/bean/copy/test/User");
            mv.visitMethodInsn(INVOKEVIRTUAL, "hh/util/bean/codegen/CGFull0", "copy", "(Lhh/util/beanutil/copy/test/User;)Ljava/lang/Object;", false);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }
}